﻿#include "pch.h"
#include "qgp/qgach.h"
#include "qg_animtex.h"

//////////////////////////////////////////////////////////////////////////
// 렌더 오브젝트
KIMPL_SUBCLASS_ABSTRACT(qgRdo, "RenderObject");


//////////////////////////////////////////////////////////////////////////
// 래스터라이저
KIMPL_SUBCLASS_ABSTRACT(qgRasz, "AbstractRasz");


//////////////////////////////////////////////////////////////////////////
// 블렌드
KIMPL_SUBCLASS_ABSTRACT(qgBlnd, "AbstractBlnd");


//////////////////////////////////////////////////////////////////////////
// 샘플러
KIMPL_SUBCLASS_ABSTRACT(qgSplr, "AbstractSplr");


//////////////////////////////////////////////////////////////////////////
// 뎁스 스텐실
KIMPL_SUBCLASS_ABSTRACT(qgDhsl, "AbstractDhsl");


//////////////////////////////////////////////////////////////////////////
// 세이더
KIMPL_SUBCLASS_ABSTRACT(qgShader, "AbstractShader");


//////////////////////////////////////////////////////////////////////////
// 레이아웃
KIMPL_SUBCLASS_ABSTRACT(qgVertexLayout, "AbstractVertexLayout");


//////////////////////////////////////////////////////////////////////////
// 버퍼
KIMPL_SUBCLASS_ABSTRACT(qgBuffer, "AbstractBuffer");

//
bool qgBuffer::SetData(kconstpointer data, kint size)
{
	k_return_value_if_fail(data, false);

	if (size == 0)
		size = _size;

	kpointer lock = Map();
	k_return_value_if_fail(lock, false);

	memcpy(lock, data, size);

	return Unmap();
}

//
bool qgBuffer::SetIndices(qgBuffer* buf, kconstpointer data, kint count, bool is32bit)
{
	k_return_value_if_fail(buf, false);
	k_return_value_if_fail(data, false);
	k_return_value_if_fail(buf->GetType() == QGBUFFER_INDEX, false);

	kint stride = buf->GetStride();

	if (count <= 0)
		count = buf->GetSize() / stride;

	kpointer lock = buf->Map();
	k_return_value_if_fail(lock, false);

	if (stride == 4)
	{
		if (is32bit)
			memcpy(lock, data, count * sizeof(kint));
		else
		{
			kushort* b16 = (kushort*)data;
			kuint* b32 = (kuint*)lock;

			for (kint i = 0; i < count; i++)
			{
				*b32 = *b16;
				b16++;
				b32++;
			}
		}
	}
	else
	{
		if (!is32bit)
			memcpy(lock, data, count * sizeof(kushort));
		else
		{
			kushort* b16 = (kushort*)lock;
			kuint* b32 = (kuint*)data;

			for (kint i = 0; i < count; i++)
			{
				*b16 = (kushort)*b32;
				b16++;
				b32++;
			}
		}
	}

	buf->Unmap();

	return true;
}

//
bool qgBuffer::SetVertices(qgBuffer* buf, kconstpointer data, kint count)
{
	k_return_value_if_fail(buf, false);
	k_return_value_if_fail(data, false);
	k_return_value_if_fail(buf->GetType() == QGBUFFER_VERTEX, false);

	if (count <= 0)
		count = buf->GetSize() / buf->GetStride();

	kpointer lock = buf->Map();
	k_return_value_if_fail(lock, false);

	memcpy(lock, data, buf->GetSize());

	buf->Unmap();

	return true;
}


//////////////////////////////////////////////////////////////////////////
// 뎁스 버퍼
KIMPL_SUBCLASS_ABSTRACT(qgDepthBuffer, "AbstractDepthBuffer");


//////////////////////////////////////////////////////////////////////////
// 텍스쳐
KIMPL_SUBCLASS_ABSTRACT(qgTexture, "AbstractTexture");


//////////////////////////////////////////////////////////////////////////
// 텍스쳐 모션

//
struct qgFwHxp
{
	kuint				count;
	kint				insts;
	kushort				framepersecond;
	kushort				width;
	kushort				height;
	kushort				bpp;
};

//
void qbServAnimTexture::_ClearStreams()
{
	// 다 필요엄쓰니 지우자
	k_delete_reset(&_mt_sizes);
	k_delete_reset(&_mt_locs);
	k_delete_reset(&_mt_data);
	kobj_reset(&_mt_ucmimg);
	kobj_reset(&_mt_stm);

	// 토탈도 지우자
	_mt_total = 0;
}

//
bool qbServAnimTexture::_ParseMotion(kStream* stm, bool preload)
{
	_mt_stm = stm;

	// 나중에 통합으로 바꿔야 함
	qbFwHsItem him;
	if (stm->Read(&him, 0, sizeof(qbFwHsHead)) != sizeof(qbFwHsHead) ||
		him.hdr != K_MAKEFOURCC('H', 'X', 'P', ' ') ||
		him.type != QGHI_HXP ||
		him.version != 1)
		return false;

	//
	qgFwHxp hxp;
	if (stm->Read(&hxp, 0, sizeof(qgFwHxp)) != sizeof(qgFwHxp) ||
		hxp.count == 0 ||
		hxp.framepersecond == 0 ||
		hxp.width == 0 ||
		hxp.height == 0 ||
		hxp.bpp != 4)
		return false;

	// 개별 길이 저장
	_mt_sizes = k_new(hxp.count, kint);
	if (stm->Read(_mt_sizes, 0, sizeof(kint)* hxp.count) != (kint)(sizeof(kint)* hxp.count))
		return false;

	// 누적 seek 위치 저장
	_mt_locs = k_new(hxp.count, kint);
	_mt_locs[0] = (kint)stm->Tell();
	kint readsize = _mt_sizes[0];

	for (kuint i = 1; i < hxp.count; i++)
	{
		_mt_locs[i] = _mt_locs[i - 1] + _mt_sizes[i - 1];

		if (readsize < _mt_sizes[i])
			readsize = _mt_sizes[i];
	}

	// 기본 프로퍼티
	_mt_total = him.length;
	_mt_count = hxp.count;
	_mt_fps = hxp.framepersecond;
	_mt_bpp = hxp.bpp;

	_mt_fbv = (float)_mt_fps;
	_mt_len = (float)(1.0f / _mt_fbv * _mt_count);
	_mt_range = tpl::Point(0, _mt_count - 1);
	_mt_loop = false;
	_mt_pause = false;

	_mt_adv = 0.0f;
	_mt_index = K_MAX_UINT;
	_mt_next = K_MAX_UINT;

	_mt_data = k_new(readsize, kbyte);

	_mt_ucmimg = kobj_new(qgImage, hxp.width, hxp.height, QGCF32_RGBA);

	// 너무 크면 좀 그렇슴
	if (preload)
	{
		klong size = stm->GetSize();
		if (size < 67108864)	// 64*1024*1024=64메가
			_mt_preimgs.resize(_mt_count);
	}

	return true;
}

//
bool qbServAnimTexture::_UpdateStreamImage(kuint image)
{
	if (!_mt_ucmimg)
		return false;

	if (!_mt_stm->Seek(_mt_locs[image], KSEEK_BEGIN))
		return false;

	kint size = _mt_sizes[image];
	if (_mt_stm->Read(_mt_data, 0, size) != size)
		return false;

	if (!k_zucmprsb(_mt_ucmimg->GetData(), _mt_ucmimg->GetSize(), _mt_data, size))
		return false;

	return true;
}

//
bool qbServAnimTexture::_UpdateImage(kuint image)
{
	if (image < _mt_range.umin)
		image = _mt_range.umin;
	else if (image > _mt_range.umax)
		image = _mt_range.umax;

	if (image == _mt_index)
		return false;

	_mt_index = image;

	return _UpdateStreamImage(image);
}

//
bool qbServAnimTexture::_UpdateNextImage()
{
	return _UpdateImage(_mt_next);
}

//
bool qbServAnimTexture::_UpdateAdvance(float f)
{
	_mt_adv += f;

	_mt_next = _mt_range.umin + (kuint)(_mt_adv * _mt_fbv);

	return _mt_adv < _mt_len;
}

//
void qbServAnimTexture::_ResetLoop()
{
	float f = _mt_adv;

	for (;;)
	{
		f -= _mt_len;
		if (f < _mt_len)
			break;
	}

	_mt_adv = f;
	_mt_next = _mt_range.umin + (kuint)(_mt_adv * _mt_fbv);
}

//
void qbServAnimTexture::Reset()
{
	_mt_adv = 0.0f;
}

//
void qbServAnimTexture::SetFixedLength(float f)
{
	_mt_len = f;
	_mt_fixed = f;
}

//
void qbServAnimTexture::SetFps(kint v)
{
	k_return_if_fail(v >= 1);

	_mt_fps = v;
	_mt_fbv = (float)v;

	if (_mt_fixed > 0.0f)
		_mt_len = _mt_fixed;
	else
	{
		kuint tmp = _mt_range.umax - _mt_range.umin + 1;
		_mt_len = 1.0f / _mt_fbv * (float)tmp;
	}
}

//
void qbServAnimTexture::SetFrameRange(kint smin, kint smax)
{
	_mt_range.umin = (kuint)K_CLAMP(smin, 0, (kint)_mt_count - 1);
	_mt_range.umax = (kuint)K_CLAMP(smax, 0, (kint)_mt_count - 1);

	if (_mt_range.umax == 0)
		_mt_range.umax = _mt_count - 1;

	if (_mt_fixed > 0.0f)
		_mt_len = _mt_fixed;
	else
	{
		kuint tmp = _mt_range.umax - _mt_range.umin + 1;
		_mt_len = 1.0f / _mt_fbv * (float)tmp;
	}
}

//
void qbServAnimTexture::SetPosition(float pos)
{
	if (pos < 0.0f || pos > _mt_len)
		return;

	_mt_adv = pos;
}

//
void qbServAnimTexture::SetPlayMode(bool isplay)
{
	_mt_pause = !isplay;
}

//
void qbServAnimTexture::TurnLoop(bool isloop)
{
	_mt_loop = isloop;
}

//
bool qbServAnimTexture::ManualMotionUpdate(float adv, bool& res, kuintptr& ptr)
{
	// res = 리소스를 업데이트하나... 스트리밍일 경우 true는 텍스쳐 만들기
	// ptr = 리소스 데이터... 리스트 캐시일 경우 텍스쳐 리소스 포인터

	if (!_UpdateAdvance(adv))
	{
		if (!_mt_loop)
		{
			ptr = 0;
			res = false;
			return false;
		}

		_ResetLoop();
	}

	if (_mt_preimgs.empty())
	{
		// 스트리밍
		ptr = 0;

		if (_mt_next == _mt_index ||
			_mt_next > _mt_range.umax)
		{
			res = false;
			return true;
		}

		_mt_index = _mt_next;

		if (!_UpdateStreamImage(_mt_next))
		{
			res = false;
			return false;
		}
		else
		{
			res = true;
			return true;
		}
	}
	else
	{
		if (_mt_next > _mt_range.umax)
			_mt_index = _mt_range.umax;
		else
			_mt_index = _mt_next;

		ptr = _mt_preimgs[_mt_index];
		res = false;

		return true;
	}
}

//
qgImage* qbServAnimTexture::MotionImage(kint index)
{
	if (!_mt_stm)
		return NULL;

	if (index < 0)
		index = _mt_index;

	if (index < 0 || (kuint)index >= _mt_count)
		return NULL;

	//
	if (!_mt_stm->Seek(_mt_locs[index], KSEEK_BEGIN))
		return NULL;

	kint size = _mt_sizes[index];
	if (_mt_stm->Read(_mt_data, 0, size) != size)
		return NULL;

	//
	qgImage* img = kobj_create(qgImage);
	img->Initialize(_mt_ucmimg->GetWidth(), _mt_ucmimg->GetHeight(), QGCF32_RGBA);

	if (!k_zucmprsb(img->GetData(), img->GetSize(), _mt_data, size))
	{
		img->Unload();
		return NULL;
	}

	return img;
}
